//
//  TwitterEngineManager.m
//  TwitterPlane
//
//  Created by kazuyuki takahashi on 09/12/13.
//  Copyright 2009 by invisibledesigner.com. All rights reserved.
//

#import "TwitterEngineManager.h"
#import "MGTwitterEngine.h"
#import "Status.h"
#import "PreferencesController.h"
#import "Notifications.h"
#import "NSUserDefaults+App.h"
#import "NSString+ull.h"
#import "OAConsumer+AppSpecific.h"
#import "OAToken_KeychainExtensions.h"


static const NSUInteger kNumMaxStatuses = 100;


@implementation TwitterEngineManager

@dynamic statusesController, autoReloadEnabled;

+ (id)defaultManager
{
    static dispatch_once_t pred;
    static TwitterEngineManager *mgr = nil;
    dispatch_once(&pred, ^{
        mgr = [[self alloc] init];
    });
    return mgr;
}

- (id)init
{
    self = [super init];
    if (self) {
        engine_ = [[MGTwitterEngine alloc] initWithDelegate:self];
        callbacks_ = [[NSMutableDictionary alloc] init];
        twitConnectionIDs_ = [[NSMutableSet alloc] init];
        deleteConnectionInfos_ = [[NSMutableDictionary alloc] initWithCapacity:1];
    }
    return self;
}

- (NSArrayController*)statusesController
{
    return statusesController_;
}

- (void)setStatusesController:(NSArrayController *)controller
{
    NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:@"statusID" 
                                                         ascending:NO 
                                                        comparator:[Status statusIDComparator]];
    [controller setSortDescriptors:[NSArray arrayWithObject:sd]];
    statusesController_ = controller;
}

- (BOOL)isAutoReloadEnabled
{
    return (autoReloadTimer_ != nil);
}

- (void)setAutoReloadEnabled:(BOOL)enabled
{
    if (enabled) {
        if (autoReloadTimer_ != nil) {
            return;
        }
        autoReloadTimer_ = [[NSTimer scheduledTimerWithTimeInterval:30
                                                             target:self
                                                           selector:@selector(getFollowedTimeline)
                                                           userInfo:nil
                                                            repeats:YES] retain];
    } else {
        [autoReloadTimer_ invalidate];
        [autoReloadTimer_ release];
        autoReloadTimer_ = nil;
    }
}

- (void)getFollowedTimeline
{
    OAConsumer *consumer = [OAConsumer consumer];
    [engine_ setConsumerKey:[consumer key] secret:[consumer secret]];
    
    OAToken *token = [[[OAToken alloc] initWithKeychainUsingAppName:@"twitplane"
                                                serviceProviderName:@"twitter"] autorelease];
    if (!token) {
        return;
    }
    [engine_ setAccessToken:token];
    
    [engine_ getHomeTimelineSinceID:lastStatusID_ startingAtPage:0 count:kNumMaxStatuses];
}

- (void)reloadAllFollowedTimeline
{
    lastStatusID_ = 0;
    [statusesController_ setContent:[NSMutableArray array]];
    [self getFollowedTimeline];
}

- (void)twit:(NSString *)text callback:(TwitterEngineCallback)callback
{
    NSParameterAssert([text length] && callback);
    NSString *conid = [engine_ sendUpdate:text];
    [callbacks_ setObject:callback forKey:conid];
    [twitConnectionIDs_ addObject:conid];
}

- (void)twit:(NSString *)text replyToStatus:(Status *)status callback:(TwitterEngineCallback)callback
{
    NSParameterAssert([text length] && callback);
    unsigned long long stid = [[status statusID] unsignedLongLongValue];
    NSString *conid = [engine_ sendUpdate:text inReplyTo:stid];
    [callbacks_ setObject:callback forKey:conid];
    [twitConnectionIDs_ addObject:conid];
}

- (void)deleteStatus:(Status *)status callback:(TwitterEngineCallback)callback
{
    NSParameterAssert(status && callback);
    unsigned long long stid = [[status statusID] unsignedLongLongValue];
    NSString *conid = [engine_ deleteUpdate:stid];
    [callbacks_ setObject:callback forKey:conid];
    [deleteConnectionInfos_ setObject:status forKey:conid];
}

- (void)checkUserCredentials
{
    [engine_ checkUserCredentials];
}

- (void)dealloc
{
    [engine_ release];
    [callbacks_ release];
    [twitConnectionIDs_ release];
    [deleteConnectionInfos_ release];
    [autoReloadTimer_ invalidate];
    [autoReloadTimer_ release];
    [super dealloc];
}


#pragma mark -
#pragma mark MGTwitterEngineDelegate methods

- (void)requestSucceeded:(NSString *)connectionIdentifier
{
    //オートリロードが停止していたら再開させる
    [self setAutoReloadEnabled:YES];
    
    //削除成功時
    Status *deleted = [deleteConnectionInfos_ objectForKey:connectionIdentifier];
    if (deleted) {
        [statusesController_ removeObject:deleted];
        [[NSNotificationCenter defaultCenter] postNotificationName:TwitterEngineManagerDidReceiveStatusNotification
                                                            object:self];
    }
    
    TwitterEngineCallback callback = [callbacks_ objectForKey:connectionIdentifier];
    if (callback) {
        callback(YES, nil);
    }
}

- (void)requestFailed:(NSString *)connectionIdentifier withError:(NSError *)error
{
    TwitterEngineCallback callback = [callbacks_ objectForKey:connectionIdentifier];
    if (callback) {
        callback(NO, error);
    } else {
        //認証失敗時はエラーメッセージを表示、オートリロード停止、Preferences開く
        if ([[error domain] isEqualToString:@"HTTP"] && [error code] == 401) {
            [self setAutoReloadEnabled:NO];
            NSString *msg = NSLocalizedString(@"AuthenticationErrorMessage", @"");
            NSAlert *alert = [NSAlert alertWithMessageText:msg
                                             defaultButton:@"OK"
                                           alternateButton:nil
                                               otherButton:nil
                                 informativeTextWithFormat:@""];
            [alert runModal];
            [[PreferencesController sharedController] showWindow:self];
        }
    }
}

- (void)statusesReceived:(NSArray *)statuses forRequest:(NSString *)connectionIdentifier
{
    //削除時にも受信するらしいので弾く
    if ([deleteConnectionInfos_ objectForKey:connectionIdentifier]) {
        return;
    }
    
    NSAssert(statusesController_ != nil, @"initialization error");
    DevLog(@"NewStatuses: %d", [statuses count]);
    
    NSMutableArray *received = [NSMutableArray arrayWithCapacity:[statuses count]];
    NSArray *sorted = [statusesController_ arrangedObjects];
    const NSRange sortedRange = NSMakeRange(0, [sorted count]);
    NSComparator cmp = ^(id obj1, id obj2) {
        Status *s = (Status*)obj1;
        NSString *sid = (NSString*)obj2;
        return [sid compare:[s statusID]];
    };
    
    for (NSDictionary *stdic in statuses) {
        //既に存在していないかチェック
        NSString *stid = [Status statusIDWithDictionary:stdic];
        NSUInteger index = [sorted indexOfObject:stid
                                   inSortedRange:sortedRange
                                         options:0
                                 usingComparator:cmp];
        if (index == NSNotFound) {
            Status *s = [[[Status alloc] initWithDictionary:stdic] autorelease];
            [received addObject:s];
        }
    }
    
    if ([received count] == 0) {
        return;
    }
    
    [statusesController_ addObjects:received];
    [statusesController_ rearrangeObjects];
    
    if (![twitConnectionIDs_ containsObject:connectionIdentifier]) {
        //twit時のロードでなければ最新updateIDを更新
        const unsigned long long sid = [[[[statusesController_ arrangedObjects] objectAtIndex:0] statusID] longLongValue];
        lastStatusID_ = (lastStatusID_ > sid) ? lastStatusID_ : sid;
    }
    
    //ふえすぎたら消す
    const NSUInteger count = [[statusesController_ content] count];
    if (count > kNumMaxStatuses) {
        NSRange removeRange = NSMakeRange(kNumMaxStatuses, count - kNumMaxStatuses);
        NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:removeRange];
        [statusesController_ removeObjectsAtArrangedObjectIndexes:indexes];
    }
    
    if ([received count] > 0) {
        [[NSNotificationCenter defaultCenter] postNotificationName:TwitterEngineManagerDidReceiveStatusNotification
                                                            object:self];
    }
}

- (void)userInfoReceived:(NSArray*)userInfo forRequest:(NSString *)connectionIdentifier
{
    //ここにくるのはverify_credentialsのときだけ・・・のはず
    //自分のscreennameを取得
    NSString *name = [[userInfo objectAtIndex:0] objectForKey:@"screen_name"];
    [User setMyScreenName:name];
}

- (void)connectionFinished:(NSString *)connectionIdentifier
{
    [twitConnectionIDs_ removeObject:connectionIdentifier];
    [deleteConnectionInfos_ removeObjectForKey:connectionIdentifier];
    [callbacks_ removeObjectForKey:connectionIdentifier];
}

@end
